package jTopaz;

import org.globus.ftp.GridFTPClient;
import org.ietf.jgss.GSSCredential;
import java.io.File;
import org.globus.gsi.GlobusCredential;
import org.globus.gsi.gssapi.GlobusGSSCredentialImpl;
import org.globus.myproxy.MyProxy;
import java.util.LinkedList;

/**
Patrick McClory.
This class wraps up CoG functionality into one object.  Creates threads for creating/authenticating clients.  Creates threads for transfers.  Maintains a queue of transfers.  Contains methods to see if threads have completed execution.
*/
public class GridFTPClientWrapper {


	//private fields - I don't think I'm using all of these anymore, so should be cleaned up.
    private GridFTPClient infoClient;
    private GridFTPClient transferClient;
    private boolean inProgress=false;
    private double speed=0;
    private double progress=0;
    private LocalFileMonitor localMonitor=null;
    private DownloadThread downloadTransfer;
    private String transferName;
    private LinkedList<String[]> transferQueue=new LinkedList<String[]>();
    private LinkedList<String[]> finishedJobs=new LinkedList<String[]>();
    private boolean loggedIn=false;
	private LoginThread loginComponent;
	private GridFTPClient uploadClient;
	private RemoteFileMonitor remoteMonitor=null;
	private String uploadName;
	//0 for download, 1 for upload
    private int transferType;
	private UploadThread uploadTransfer;
	private GSSCredential cred;
	private MyProxyThread mpThread;
	private LoginThread transferLoginThread;
	private LoginThread uploadLoginThread;
	//private boolean infoReady;
	private boolean tAuthStarted=false;
	private boolean uAuthStarted=false;
	private boolean infoReady=false;
	private boolean transferReady=false;
	private boolean uploadReady=false;
	private boolean firstTransfer;
	private boolean firstUpload;
	
	/**
	Creates a MyProxyThread and starts it's execution.  This handles logging on to the myProxy server.
	@param String myProxyHost the name of the myproxy server you want to connect to.
	*/
	public void myProxyLogin(String myProxyHost, String username, String password) {
		
		mpThread = new MyProxyThread(myProxyHost, username, password);
		mpThread.start();
		
	}
	
	/**
	Gets the status of logging onto the myproxy server
	@return "success" if successfully logged on and obtained credential
	*/
	public String getMyProxyStatus() {
		if (mpThread.getStatus() == "success") {
			cred = mpThread.getCred();	
		}
		return mpThread.getStatus();	
	}
	
	/**
	Starts the login thread of the info client
	@param host the name of the host to connect to
	*/
	public void remoteLogin(String host) {
		loginComponent = new LoginThread(host, cred);
		loginComponent.start();
	}
	
	/**Starts the login thread of the transfer client.
	@param host the name of the host to connect to.
	*/
	public void transferClientLogin(String host) {
		transferLoginThread = new LoginThread(host, cred);
		transferLoginThread.start();
		
	}
	
	/**Starts the login thread of the upload client.
	@param host the name of the host to connect to.
	*/
	public void uploadClientLogin(String host) {
		uploadLoginThread = new LoginThread(host, cred);
		uploadLoginThread.start();
	}
	
	/**
	Gets the status of the info client log on thread.
	@return "success" if successfully logged on.  "failed" if there was an error.  Will be null if not finished executing.
	*/	
	public String getLoginStatus() {
		
		return loginComponent.getStatus();	
	}
	
	/**
	Gets the status of the transfer client log on thread.
	@return "success" if successfully logged on.  "failed" if there was an error.  Will be null if not finished executing.
	*/	
	public String getTransferClientStatus() {
		
		return transferLoginThread.getStatus();
	}
	
	/**
	Gets the status of the upload client log on thread.
	@return "success" if successfully logged on.  "failed" if there was an error.  Will be null if not finished executing.
	*/	
	public String getUploadClientStatus() {
		
		return uploadLoginThread.getStatus();
	}
	
	
	/*
	These upcoming methods are essentially identical to the last 3, should go with one or the other.  and delete whichever one you don't use.
	*/
	
	/**
	Returns whether or not this object is ready to handle upload transfers.
	@return true if it can.
	*/
	public boolean isUploadReady() {
		try{
		if (uploadLoginThread.getStatus() == "success") {
			uploadReady = true;	
		}
		
		return uploadReady;
		}
		catch (Exception e) {
			return false;
		}
	}
	
	/**
	Returns whether or not this object is ready to handle transfers (downloads).
	@return true if it can
	*/
	public boolean isTransferReady() {
		try {
		if (transferLoginThread.getStatus() == "success") {
			transferReady = true;	
		}
		return transferReady;
		}
		catch (Exception e) {
			return false;
		}
	}
	
	/**
	Returns whether or not this object is ready to obtain remote info (for directory browsing).
	@return true if it can
	*/
	public boolean isInfoReady() {
		try {
		if (loginComponent.getStatus() == "success") {
			infoReady = true;	
		}
		return infoReady;
		}
	
		catch (Exception e) {
			return false;
		}
	}
	
	/**
	Sets the transfer client as the client created during the execution of the transfer login thread.
	*/
	public void setTransferClient() {
		transferClient = transferLoginThread.getClient();
		//change this later to something else
		firstTransfer = false;	
	}
	
	/**
	Sets the upload client as the client created during the execution of the upload login thread.
	*/
	public void setUploadClient() {
		uploadClient = uploadLoginThread.getClient();
		
	}
	
	/**
	Returns whether or not this is the first transfer.
	*/
	public boolean isFirstTransfer() {
		return firstTransfer;
	}
	
	/**
	Returns whether or not this is the first upload.
	*/
	public boolean isFirstUpload() {
		return firstUpload;	
	}
	
	
    //update
    /**
    Updates the info client.  Resets the server into passive mode, and the client into active mode.
    @return "success" if successful.
    */
    public String update() {
	
		if (infoClient == null) {
			//System.out.println("in here?");
			infoClient = loginComponent.getClient();
		}
	//System.out.println("die in here?");
		try {
			infoClient.setPassive();
			infoClient.setLocalActive();
			System.out.println("set client active/server passive passed!");

			return "success";
		}
		catch (Exception e) {
	    	return e.toString();
		}
    }

	/**
	Calls the GridFTPClient list method to get the contents of the current dir.
	@return an array of objects corresponding the contents.
	*/
    public Object[] list() {
	
		try {
	    	Object[] contents = infoClient.list().toArray();
	    	return contents;
		}

		catch (Exception e) {
	    	System.out.println(e);
	    	return null;
		}
    }

	/**
	Adds a new transfer to the transfer queue.
	@param command the name of the command (either "upload" or "download")l
	@param remoteFile the full name (path) of the remoteFile to upload to or download from.
	@param localFile the full name (path) of the local file to upload from or download to.
	*/
    public void addToQueue(String command, String remoteFile, String localFile) {
		String[] job = new String[3];
		job[0] = command;
		job[1] = remoteFile;
		job[2] = localFile;
		transferQueue.add(job);
		System.out.println("job added to the queue");
    }

	/**
	Returns whether or not the next transfer is ready to take place.  Makes sure no job is currently in progress, and there is another job to run.
	*/
    public boolean queueReady() {
		//System.out.println("in here");
		if (inProgress) {
	    	return false;
		}
		//check if there is a command to run
		else if (transferQueue.size() > 0) {
	    	System.out.println("queue size: " + transferQueue.size());
	    	return true;
		}
		else 
	    	return false;
    	}

	/**
	Starts the next transfer in the queue.
	*/
    public void StartTransfer() {
		String[] command = transferQueue.removeFirst();

		//System.out.println("in here?");
		//System.out.println(command[0]);
		//System.out.println(command[1]);
		//System.out.println(command[2]);

		if(command[0].compareTo("download") == 0) {
	    	File localFile = new File(command[2]);
	 	   download(command[1], localFile);
		}
		
		else if (command[0].compareTo("upload") == 0) {
			//System.out.println(command[1]);
			//System.out.println(command[2]);
			File localFile = new File(command[1]);
			//System.out.println("wtf is going on?");
			upload(command[2], localFile);	
		}
    }
    
    //I think this should be changed to private.
    /**
    Creates a DownloadThread object and begins it's execution.  Also creates a LocalFileMonitor and begins it's execution.
    @param remoteFileName the name of the remoteFile to download (full path).
    @param localFile the file object to download to.
    @return "success" if the download was successfully started.
    */
    public String download(String remoteFileName, File localFile) {

		try {
	    	inProgress = true;
	    	transferType =0;
	    	//client.get(remoteFileName, localFile);
	    
	    	transferName = localFile.getName();
	    
	    	//for now just overwrite it, get rid of this for a check later
	    	if(localFile.exists()) {
			localFile.delete();
	    	}

	    	localFile.createNewFile();

	    	long remoteSize = infoClient.getSize(remoteFileName);
	    	localMonitor = new LocalFileMonitor(localFile.getPath(), remoteSize);

			System.out.println(transferClient);
	    	downloadTransfer = new DownloadThread(transferClient, localFile, remoteFileName, remoteSize);
	    	
	    	downloadTransfer.start();
	    	localMonitor.run();
	    	
	    	System.out.println("transfer successfully started");
	    	
	    	String[] finished = new String[2];
	    	finished[0] = localFile.getName();
	    	finished[1] = Integer.toString(finishedJobs.size());
	    	finishedJobs.add(finished);
	    	if (firstTransfer == true) {
	    		firstTransfer = false;
	    	}
	    	return "success";
		}
	
		catch (Exception e) {
	    	inProgress = false;
	    	speed = 0;
	    	progress = 0;
	    	//System.out.println(e);
	    	return(e.toString());
		}
    }

	//this should also probably be switched to private.  only calling it from within here.
	/**
	Creates a UploadThread object and begins it's execution.  Also creates a RemoteFileMonitor object and being it's execution.
	@param remoteFile the full path of the remoteFile to upload to.
	@param localFile the File object that serves as the souce 
	@return "success" if the upload was successfully started.
	*/
	public String upload(String remoteFile, File localFile) {
		inProgress = true;
		transferType = 1;
		
		transferName = localFile.getName();
		
		long localSize = localFile.length();
		
		try {

		
		remoteMonitor = new RemoteFileMonitor(remoteFile, localSize, uploadClient, cred);
		
		}
		catch (Exception e) {
			
		}
		
		uploadTransfer = new UploadThread(transferClient, localFile, remoteFile);
		
		uploadTransfer.start();
		remoteMonitor.run();
		
		System.out.println("Upload successfully started");
		
		String[] finished = new String[2];
		finished[0] = localFile.getName();
		finished[1] = Integer.toString(finishedJobs.size());
		finishedJobs.add(finished);
		
		if (firstTransfer == true) {
			firstTransfer = false;
		}
		if (firstUpload == true) {
			firstUpload = false;
		}
		return "success";
		
	}
	
	/**
	Checks to see if a transfer is currently in progress.
	*/
    public boolean transferInProgress() {
    	if(transferType == 0) {
			if(downloadTransfer != null) {
	    		inProgress = (!downloadTransfer.transferComplete());
	    		if (!inProgress) {
	    			localMonitor.stop();	
	    		}
	    		return inProgress;
			}
		else
	    	return false;
    	}
    	else if (transferType == 1) {
    		if(uploadTransfer != null) {
	    		inProgress = (!uploadTransfer.transferComplete());
	    		//if it's done
	    		if (!inProgress) {
	    			//System.out.println("stopping the monitor");
	    			remoteMonitor.stop();
	    		}
	    		return inProgress;
			}
			else
	    		return false;
    	}
    	else
    		return false;
    }

	/**
	Returns the last completed transfer.
	return a string array containing the name of the transfer type (upload or download), the remote file name, and the local file name.  null if no file has been transferred
	*/
    public String[] getLastCompletedTransfer() {
		if (finishedJobs.size() > 0) {
	    	return finishedJobs.getLast();
		}
		else {
	    	return null;
		}
    }

	/**
	return the name of the last transfer.
	*/
    public String getTransferName() {
		return transferName;
    }

	/**
	Gets the speed of the current transfer in kb/s.
	@return the speed.  -1 if the monitor has not been defined.
	*/
    public double getSpeed() {
    
   		if (transferType == 0) {
			if (localMonitor != null) {
	    		speed = localMonitor.getSpeed();
	    		return speed;
			}
			else {
	    		return -1;
			}
   		}
   		else if (transferType == 1) {
   			if (remoteMonitor != null) {
   				speed = remoteMonitor.getSpeed();
   				return speed;
   			}
   			else {
   				return -1;
   			}
   		}
   		else {
   			return -1;
   		}
    }

	/**
	Gets the percent complete of the current transfer.
	@return the progress.  -1 if the monitor wasn't defined.
	*/
    public double getProgress() {
	   	if (transferType == 0) {
			if (localMonitor != null) {
	    		progress = localMonitor.getProgress();
	    		return progress;
			}
			else {
	    		return -1;
			}
   		}
   		else if (transferType == 1) {
   			if (remoteMonitor != null) {
   				progress = remoteMonitor.getProgress();
   				return progress;
   			}
   			else {
   				return -1;
   			}
   		}
   		else {
   			return -1;
   		}
    }


	/**
	Changes the info clients current directory.
	@param dir the destination directory.
	@return "success" if successfully able to change dir.
	*/
    public String changeDir(String dir) {

		try {
	    	infoClient.changeDir(dir);
	    	return "success";
		}

		catch(Exception e) {
	    	System.out.println("Failed changing dir");
	    	e.printStackTrace();
	    	return(e.toString());
		}
    }

	/**
	Gets the info clients current directory
	@return the current dir or "failed" if there was a problem.
	*/
    public String getCurrentDir() {

		try {
	    	return infoClient.getCurrentDir();
		}

		catch(Exception e) {
	    	return "failed";
		}
    }

	/**
	Changes the info client to the parent dir of the current dir.
	@return "success" if successful
	*/
    public String goUpDir() {
	
		try {
	    	infoClient.goUpDir();
	    	return "success";
		}
	
		catch(Exception e) {
	    	System.out.println("Failed going up dir");
	    	e.printStackTrace();
	    	return "failed";
		}
    }

	/**
	Closes the clients connections with the remote server.
	@return "success" if successful.
	*/
    public String close() {

		try {
	    	infoClient.close();
	   		transferClient.close();
			uploadClient.close();
			
	    	System.out.println("closed");
	    	return "success";
		}
		catch (Exception e) {
	    	return e.toString();
		}
    }
}
